#ifndef JSON_H
#define JSON_H

#include "block_allocator.h"

enum json_type
{
	JSON_NULL = 0,
	JSON_OBJECT,
	JSON_ARRAY,
	JSON_STRING,
	JSON_INT,
	JSON_FLOAT,
	JSON_BOOL,
};

struct json_value
{
	json_value *parent;
	json_value *next_sibling;
	json_value *first_child;
	json_value *last_child;

	char *name;
	union
	{
		char *string_value;
		int int_value;
		float float_value;
	};

	json_type type;
};

json_value *json_parse(char *source, char **error_pos, char **error_desc, int *error_line, block_allocator *allocator);

namespace json
{	
	class value;

	class value
	{
		json_value* vp;
	protected:		
		value(json_value* src = 0) : vp(0)
		{
			reset(src);
		}

		void reset(json_value* ptr = 0)
		{
			static json_value empty_value = {0};
			vp = (ptr ? ptr : &empty_value);
		}

	public:
		value child() {return vp->first_child;}
		value operator()(const char* child_name);
		
		value sibling() {return vp->next_sibling;}
		value sibling(const char* name);
		value operator[](const char* sibling_name) {return sibling(sibling_name);}	

		value operator[](unsigned int array_element_index);
		value array_first() {return type() == JSON_ARRAY ? child() : value();}		
		
		value parent() {return vp->parent;}	

		operator bool() const {return !(!vp->parent && !vp->first_child);}

		const char* name() const {return vp->name;}

		json_type type() const {return vp->type;}

		bool object() const {return type() == JSON_OBJECT;}
		bool array() const {return type() == JSON_ARRAY;}

		bool as_bool() const {return type() == JSON_BOOL ? (vp->int_value > 0) : false;}
		int as_int() const {return type() == JSON_INT ? vp->int_value : 0;}
		float as_float() const {return type() == JSON_FLOAT ? vp->float_value : .0f;}
		const char* as_str() const {return str();}

		const char* str() const {return type() == JSON_STRING ? vp->string_value : 0;}		
		const char* c_str() const {return str();}
	};

	class document : public value
	{
	public:
		document();
		bool load(const char* file_name);
		bool parse(char* data, char **error_pos = 0, char **error_desc = 0, int *error_line = 0);

	private:
		block_allocator allocator;
	};
}

#endif
